package com.blue.base.oauth.common.config;

import com.blue.base.common.jwt.RsaKeyUtil;
import com.blue.base.oauth.common.bean.RemoteGetUserInfo;
import com.blue.base.oauth.common.dto.AuthUserDetailDto;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * jwt-token模式配置
 *
 * @author liulei
 * @version 1.0
 */
@Slf4j
@Configuration
public class TokenConfig {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ObjectProvider<UserDetailsService> userDetailsServices;
    //==================================================分布式session模式/==================================================

    /**
     * 授权服务器端，TokenService
     */
    @Bean
    @ConditionalOnProperty(value = "oauth2.type.session.role", havingValue = "server")
    public TokenStore redisTokenStore() {
        log.info("使用  --->  RedisTokenStore 存储token");
        RedisTokenStore tokenStore = new RedisTokenStore(redisTemplate.getConnectionFactory());
        tokenStore.setPrefix("cloud:");
        return tokenStore;
    }

    /**
     * token转换器
     */
    @Bean
    @ConditionalOnProperty(value = "oauth2.type.session.role", havingValue = "server")
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            // 此处的bean转换务必和AS授权服务器的UserDetailService的方法loadUserByUsername对应上
            AuthUserDetailDto user = (AuthUserDetailDto) authentication.getPrincipal();
            RemoteGetUserInfo simpleUser = new RemoteGetUserInfo();
            BeanUtils.copyProperties(user, simpleUser);
            final Map<String, Object> additionalInfo = new HashMap<>(8);
            additionalInfo.put("user", simpleUser);
            log.info("token返回增强，返回用户信息:{}", simpleUser);
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            return accessToken;
        };
    }

    /**
     * 资源服务器TokenService
     */
    @Bean
    @ConditionalOnProperty(value = "oauth2.type.session.role", havingValue = "resource")
    public ResourceServerTokenServices tokenServices(ResourceConfig OAuth2Config) {
        RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setClientId(OAuth2Config.getClientId());
        tokenServices.setClientSecret(OAuth2Config.getClientSecret());
        tokenServices.setCheckTokenEndpointUrl(OAuth2Config.getCheckTokenUrl());
        return tokenServices;
    }

    //==================================================JWT模式/==================================================

    /**
     * jwtTokenStore
     */
    @Bean
    @ConditionalOnProperty(value = "oauth2.type", havingValue = "jwt")
    public TokenStore jwtTokenStore() {
        log.info("使用  --->  JwtTokenStore 存储token");
        return new JwtTokenStore(accessTokenConverter());
    }

    /**
     * jwtToken转换器
     */
    @Bean
    @ConditionalOnProperty(value = "oauth2.type", havingValue = "jwt")
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter() {
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
                // TODO 待解决创建/刷新token时principal不一致的问题
                String account = "";
                List<String> authorities;
                if (authentication.getPrincipal() instanceof AuthUserDetailDto) {
                    AuthUserDetailDto user = (AuthUserDetailDto) authentication.getPrincipal();
                    account = user.getAccount();
                    authorities = user.getAuthorities().stream()
                            .map(GrantedAuthority::getAuthority).collect(Collectors.toList());
                } else {
                    account = (String) authentication.getPrincipal();
                    authorities = authentication.getAuthorities().stream()
                            .map(GrantedAuthority::getAuthority).collect(Collectors.toList());
                }
                final Map<String, Object> additionalInfo = new HashMap<>(8);
                additionalInfo.put("account", account);
                additionalInfo.put("roles", authorities);
                ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
                OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
                return enhancedToken;
            }
        };
        tokenConverter.setKeyPair(RsaKeyUtil.getKeyPair());
        return tokenConverter;
    }
}
